#include "DbStorage/PackageDbStorage.hpp"
#include "DbStorage/DbStorageFactory.hpp"
#include "Model/Package.hpp"
#include "Model/Project.hpp"
#include "CaseDB/Connection.hpp"
#include "CaseDB/RecordSet.hpp"
#include "CaseDB/Statement.hpp"
#include <boost/foreach.hpp>
#include <cassert>

namespace DbStorage
{
	
PackageDbStorage::PackageDbStorage( Storage::StorageFactory& factory, CaseDB::Connection* pConnection )
	: DbStorageBase( pConnection)
	, PackageStorage( factory)
{
}

// ---------------------------------------------------------------------------------------------------------------------

void PackageDbStorage::loadPackages( Model::Project& prjToLoad )
{
	using namespace CaseDB;

	assert( !prjToLoad.storedID().empty());

	RecordSet::Metadata rsMeta;
	rsMeta.reserve( 4);
	rsMeta.push_back( DbTypesEnum::Integer);	// PK_PACKAGE
	rsMeta.push_back( DbTypesEnum::Integer);	// PK_PARENT_PACKAGE
	rsMeta.push_back( DbTypesEnum::String);		// NAME
	rsMeta.push_back( DbTypesEnum::String);		// DESCRIPTION
	RecordSet results( rsMeta);

	Statement statement( "select PK_PACKAGE, FK_PARENT_PACKAGE, NAME, DESCRIPTION from PACKAGES where FK_PROJECT = ?");
	statement.addParameter( DbTypesEnum::Integer, prjToLoad.storedID());
	dynamic_cast< DbStorageFactory&>( rStorageFactory_).dbConnection().execute( statement, results);

	PackageLookup loadedBulk;
	this->bulkLoad( results, loadedBulk);
	this->processBulk( prjToLoad, loadedBulk);
}

// ---------------------------------------------------------------------------------------------------------------------

void PackageDbStorage::bulkLoad( const CaseDB::RecordSet& results, PackageLookup& loadedBulk )
{
	CaseDB::RecordSet::ConstRecordIterator recI = results.begin(),
		endI = results.end();
	while( recI != endI)
	{
		boost::any dbParentID = (*recI)[1];
		int parentID = dbParentID.empty() ? -1 : boost::any_cast<int>( dbParentID);
		loadedBulk.insert( std::make_pair(
				parentID, 
				boost::tuples::make_tuple( 
					boost::any_cast<int>( (*recI)[0]), 
					boost::any_cast<std::string>( (*recI)[2]),
					boost::any_cast<std::string>( (*recI)[3]))));
		++recI;
	}
}

// ---------------------------------------------------------------------------------------------------------------------

void PackageDbStorage::processBulk( Model::Project& prjToLoad, PackageLookup& loadedBulk )
{
	// First, find the parent
	PackageLookup::iterator parentI = loadedBulk.find( -1);
	assert( parentI != loadedBulk.end());

	prjToLoad.rootPackage().setStoredID( parentI->second.get<0>());
	loadedBulk.erase( parentI);

	this->processChildrenOf( prjToLoad.rootPackage(), loadedBulk);
}

// ---------------------------------------------------------------------------------------------------------------------

void PackageDbStorage::processChildrenOf( Model::Package& parent, PackageLookup& loadedBulk )
{
	int curParentID = boost::any_cast<int>( parent.storedID());
	
	PackageLookup::iterator childI = loadedBulk.find( curParentID),
		endI = loadedBulk.end();

	while( childI != endI && childI->first == curParentID)
	{
		std::auto_ptr<Model::Package> pNewPackage( new Model::Package( childI->second.get<1>(), childI->second.get<2>()));
		pNewPackage->setStoredID( childI->second.get<0>());
		parent.addNewPackage( pNewPackage.get());
		this->processChildrenOf( *pNewPackage, loadedBulk);
		pNewPackage.release();
		++childI;
	}
}

// ---------------------------------------------------------------------------------------------------------------------

Model::Package* PackageDbStorage::deepSearchByStoredID( Model::Package* pCurPackage, const boost::any& storedID ) const
{
	using namespace boost;
	using namespace Model;
		
	int typedStoredID = any_cast< int>( storedID);
	Package::PackageIterator curPackageI = pCurPackage->childPackagesBegin(),
		endPackageI = pCurPackage->childPackagesEnd();
	while( curPackageI != endPackageI)
	{
		Package* pCurChild = *curPackageI;
		int curTypedStoredId = any_cast<int>( pCurChild->storedID());
		if( typedStoredID == curTypedStoredId)
		{
			return pCurChild;
		}
		else
		{
			Package* pFoundPackage = this->deepSearchByStoredID( pCurChild, storedID);
			if( NULL != pFoundPackage)
			{
				return pFoundPackage;
			}
		}
	}
	return NULL;
}


} // namespace DbStorage
